package com.smartmore.test;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;

import com.smartmore.test.MultiTouchController.MultiTouchObjectCanvas;
import com.smartmore.test.MultiTouchController.PointInfo;
import com.smartmore.test.MultiTouchController.PositionAndScale;


public class RectImageView extends ImageView implements  MultiTouchObjectCanvas<Object>  {
	
    public static final int GROW = 0;
    public static final int SHRINK = 1;
    
    public static final int TOUCH_INTERVAL = 0;    
    public static final float MIN_SCALE = 1f;
    public static final float MAX_SCALE = 4f;
    
    protected static final float MIN_DIST = 10F;
    static final float	 MIN_ZOOM	= 0.9f;
    
    private final static int NONE = 0;
    private final static int DRAG = 1;
    private final static int ZOOM = 2;

    private int touchState = NONE;
    
    PointF start = new PointF(); 
    PointF mid = new PointF(); 
    float oldDist;
    
    protected GestureDetector			mGestureDetector;
    protected GestureListener			mGestureListener;
    protected ScaleGestureDetector		mScaleDetector;
    protected ScaleListener				mScaleListener;
	protected float						mScaleFactor;
	protected float						mCurrentScaleFactor = 1f;
	float mCurrentScaleDetectorFocusX = 0f;
	float mCurrentScaleDetectorFocusY = 0f;

	
	private MultiTouchController<Object> multiTouchController;
	private PointInfo mCurrTouchPoint;
	
	//Bitmap mBitmapDisplayed = null;
	
	public float mScroollX = 0;
	public float mScroollY = 0;
	
	private Rect mImageSizeRect = null;
	
	public RectImageView(Context context) {
		super(context);
		init();
	}
	
	public RectImageView(Context context, AttributeSet attSet) {
		super(context, attSet);
		init();
	}
	
	public RectImageView(Context context, AttributeSet attSet, int defStyle) {
		super(context,attSet,defStyle);
		init();
	}
	
	public void init() {
		/*
		multiTouchController = new MultiTouchController<Object>(this);
		mCurrTouchPoint = new PointInfo();
		*/
		mScaleListener = new ScaleListener();
		mScaleDetector = new ScaleGestureDetector( getContext(), mScaleListener );
		mGestureListener = new GestureListener();
		mGestureDetector = new GestureDetector( getContext(), mGestureListener, null, true );
		
		//mMaxZoom = maxZoom();
		
		//mBitmapDisplayed = getD
	}
	
	
	
	public Rect getmImageSizeRect() {
		return mImageSizeRect;
	}

	public void setmImageSizeRect(Rect mImageSizeRect) {
		this.mImageSizeRect = mImageSizeRect;
	}

	public float getCurrentScaleDetectorFocusX() {
		return mCurrentScaleDetectorFocusX;
	}

	public void setCurrentScaleDetectorFocusX(float mCurrentScaleDetectorFocusX) {
		this.mCurrentScaleDetectorFocusX = mCurrentScaleDetectorFocusX;
	}

	public float getCurrentScaleDetectorFocusY() {
		return mCurrentScaleDetectorFocusY;
	}

	public void setCurrentScaleDetectorFocusY(float mCurrentScaleDetectorFocusY) {
		this.mCurrentScaleDetectorFocusY = mCurrentScaleDetectorFocusY;
	}

	public float getCurrentScaleFactor() {
		return mCurrentScaleFactor;
	}

	public void setCurrentScaleFactor(float mCurrentScaleFactor) {
		this.mCurrentScaleFactor = mCurrentScaleFactor;
	}
	

	@Override
	public boolean onTouchEvent( MotionEvent event )
	{
		mScaleDetector.onTouchEvent( event );
		if ( !mScaleDetector.isInProgress() ) mGestureDetector.onTouchEvent( event );
		
		//mGestureDetector.onTouchEvent( event );
		int action = event.getAction();
		switch ( action & MotionEvent.ACTION_MASK ) {
			case MotionEvent.ACTION_UP:
				Log.d("here","---------------------");
				
				if ( getScale() < 1f ) {
					zoomTo( 1f, 50 );
				}
				
				break;
		}
		return true;
	}
	
	class GestureListener extends GestureDetector.SimpleOnGestureListener {
		
		@Override
		public boolean onDoubleTap( MotionEvent e )
		{
			return super.onDoubleTap( e );
		}
		
		
		@Override
		public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY )
		{
			
			mScroollX += distanceX;
			mScroollY += distanceY;
			
			if(mScroollX < 0) {
				mScroollX =0;
			}
			
			if(mScroollY < 0) {
				mScroollY =0;
			}
			
			
			/*
			Log.d("distanceX","distanceX:"+distanceX +",distanceY:"+distanceY);	
			distanceX = (mScroollX < 0)?0:distanceX;
			distanceY = (mScroollY < 0)?0:distanceY;
			
			distanceX = (mScroollX > 1080)?0:distanceX;
			distanceY = (mScroollY > 1080)?0:distanceY;
			*/
			
			

			//Log.d("scrollXY","mScroollX:"+mScroollX +",mScroollY:"+mScroollY);
			
			//scrollBy((int)distanceX, (int)distanceY);
			
			scrollBy( -distanceX, -distanceY );		
			return super.onScroll( e1, e2, distanceX, distanceY );
		}
		
		@Override
		public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY )
		{
			return super.onFling( e1, e2, velocityX, velocityY );
		}
	}
	
	public void scrollBy( float x, float y )
	{
		panBy( x, y );
	}
	
	protected void panBy( float dx, float dy )
	{
		RectF rect = getBitmapRect();
		RectF srect = new RectF( dx, dy, 0, 0 );
		updateRect( rect, srect );
		postTranslate( srect.left, srect.top );
		//scrollBy( srect.left, srect.top );
		center( true, true );
	}
	
	protected void updateRect( RectF bitmapRect, RectF scrollRect )
	{
		float width = getWidth();
		float height = getHeight();
		
		if ( bitmapRect.top >= 0 && bitmapRect.bottom <= height ) scrollRect.top = 0;
		if ( bitmapRect.left >= 0 && bitmapRect.right <= width ) scrollRect.left = 0;
		if ( bitmapRect.top + scrollRect.top >= 0 && bitmapRect.bottom > height ) scrollRect.top = (int)( 0 - bitmapRect.top );
		if ( bitmapRect.bottom + scrollRect.top <= ( height - 0 ) && bitmapRect.top < 0 ) scrollRect.top = (int)( ( height - 0 ) - bitmapRect.bottom );
		if ( bitmapRect.left + scrollRect.left >= 0 ) scrollRect.left = (int)( 0 - bitmapRect.left );
		if ( bitmapRect.right + scrollRect.left <= ( width - 0 ) ) scrollRect.left = (int)( ( width - 0 ) - bitmapRect.right );
		// Log.d( LOG_TAG, "scrollRect(2): " + scrollRect.toString() );
		//Log.d( "scrooll", "scrollRect(2): " + scrollRect.left + ":"+ scrollRect.top );
	}
	
//	protected void scrollBy( float distanceX, float distanceY, final float durationMs )
//	{
//		final float dx = distanceX;
//		final float dy = distanceY;
//		final long startTime = System.currentTimeMillis();
//		mHandler.post( new Runnable() {
//			
//			float	old_x	= 0;
//			float	old_y	= 0;
//			
//			public void run()
//			{
//				long now = System.currentTimeMillis();
//				float currentMs = Math.min( durationMs, now - startTime );
//				float x = Cubic.easeOut( currentMs, 0, dx, durationMs );
//				float y = Cubic.easeOut( currentMs, 0, dy, durationMs );
//				panBy( ( x - old_x ), ( y - old_y ) );
//				old_x = x;
//				old_y = y;
//				if ( currentMs < durationMs ) {
//					mHandler.post( this );
//				} else {
//					RectF centerRect = getCenter( true, true );
//					if ( centerRect.left != 0 || centerRect.top != 0 ) scrollBy( centerRect.left, centerRect.top );
//				}
//			}
//		} );
//	}

	
	class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
		
		@SuppressWarnings( "unused" )
		@Override
		public boolean onScale( ScaleGestureDetector detector )
		{
			float span = detector.getCurrentSpan() - detector.getPreviousSpan();
			float targetScale = mCurrentScaleFactor * detector.getScaleFactor();
			if ( true ) {
				targetScale = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
				
				zoomTo( targetScale, detector.getFocusX(), detector.getFocusY() );
				mCurrentScaleFactor = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
				//mDoubleTapDirection = 1;
				mCurrentScaleDetectorFocusX = detector.getFocusX();
				mCurrentScaleDetectorFocusY = detector.getFocusY();
				Log.d("onScale",targetScale+":"+detector.getFocusX()+":"+detector.getFocusY()+",mCurrentScaleFactor=>"+mCurrentScaleFactor );
				invalidate();
				return true;
			}
			return false;
		}
	}
	
	protected float maxZoom()
	{
		if ( mBitmapDisplayed == null ) { return 1F; }
		float fw = (float)mBitmapDisplayed.getWidth() / (float)mThisWidth;
		float fh = (float)mBitmapDisplayed.getHeight() / (float)mThisHeight;
		float max = Math.max( fw, fh ) * 4;
		return max;
	}
	
	public float getMaxZoom()
	{
		return mMaxZoom;
	}
	
	protected void zoomTo( float scale )
	{
		float cx = getWidth() / 2F;
		float cy = getHeight() / 2F;
		zoomTo( scale, cx, cy );
	}
	
	public void zoomTo( float scale, float durationMs )
	{
		float cx = getWidth() / 2F;
		float cy = getHeight() / 2F;
		zoomTo( scale, cx, cy, durationMs );
	}
	
	
	protected Matrix						mBaseMatrix			= new Matrix();
	protected Matrix						mSuppMatrix			= new Matrix();
	protected Handler						mHandler				= new Handler();
	protected Runnable					mOnLayoutRunnable	= null;
	protected float						mMaxZoom = 4f;
	protected final Matrix				mDisplayMatrix		= new Matrix();
	protected final float[]				mMatrixValues		= new float[9];
	protected int							mThisWidth			= -1, mThisHeight = -1;
	
	//RotateBitmap		mBitmapDisplayed	= new RotateBitmap( null, 0 );
	Bitmap		mBitmapDisplayed	= null;
	final protected float				MAX_ZOOM				= 2.0f;
	
	protected enum Command {
		Center, Move, Zoom, Layout, Reset,
	};
	
	protected void onZoom( float scale )
	{
		if ( !mScaleDetector.isInProgress() ) mCurrentScaleFactor = scale;
	}
	public void zoomTo( float scale, float centerX, float centerY )
	{
		
		if ( scale > mMaxZoom ) scale = mMaxZoom;
		
		float oldScale = getScale();
		float deltaScale = scale / oldScale;
		
		
		
		Log.d("zoomTo",scale+":"+centerX+":"+centerY+":deltaScale=>"+deltaScale);
		
		postScale( deltaScale, centerX, centerY );
		onZoom( getScale() );
		center( true, true );
	}
	
	
	protected void center( boolean horizontal, boolean vertical )
	{
		//if ( mBitmapDisplayed.getBitmap() == null ) return;
		RectF rect = getCenter( horizontal, vertical );
		if ( rect.left != 0 || rect.top != 0 ) {
			postTranslate( rect.left, rect.top );
		}
	}
	
	protected RectF getBitmapRect()
	{
		if ( mBitmapDisplayed == null ) return null;
		Matrix m = getImageViewMatrix();
		RectF rect = new RectF( 0, 0, mBitmapDisplayed.getWidth(), mBitmapDisplayed.getHeight() );
		m.mapRect( rect );
		return rect;
	}
	
	protected RectF getCenter( boolean horizontal, boolean vertical )
	{
		if ( mBitmapDisplayed == null ) return new RectF( 0, 0, 0, 0 );
		RectF rect = getBitmapRect();
		
		float height = rect.height();
		float width = rect.width();
			
		
		float deltaX = 0, deltaY = 0;
		if ( vertical ) {
			int viewHeight = getHeight();
			if ( height < viewHeight ) {
				deltaY = ( viewHeight - height ) / 2 - rect.top;
			} else if ( rect.top > 0 ) {
				deltaY = -rect.top;
			} else if ( rect.bottom < viewHeight ) {
				deltaY = getHeight() - rect.bottom;
			}
		}
		if ( horizontal ) {
			int viewWidth = getWidth();
			if ( width < viewWidth ) {
				deltaX = ( viewWidth - width ) / 2 - rect.left;
			} else if ( rect.left > 0 ) {
				deltaX = -rect.left;
			} else if ( rect.right < viewWidth ) {
				deltaX = viewWidth - rect.right;
			}
		}
		return new RectF( deltaX, deltaY, 0, 0 );
	}
	
	@Override
	public void setImageBitmap( Bitmap bitmap )
	{
		setImageBitmap( bitmap, 0 );
	}
	
	/**
	 * This is the ultimate method called when a new bitmap is set
	 * @param bitmap
	 * @param rotation
	 */
	protected void setImageBitmap( Bitmap bitmap, int rotation )
	{
		super.setImageBitmap( bitmap );
		Drawable d = getDrawable();
		if ( d != null ) {
			d.setDither( true );
		}
		mBitmapDisplayed = bitmap;
//		mBitmapDisplayed.setBitmap( bitmap );
//		mBitmapDisplayed.setRotation( rotation );
	}
	
	protected Matrix getImageViewMatrix()
	{
		mDisplayMatrix.set( mBaseMatrix );
		mDisplayMatrix.postConcat( mSuppMatrix );
		return mDisplayMatrix;
	}
	
	protected void postScale( float scale, float centerX, float centerY )
	{
		mSuppMatrix.postScale( scale, scale, centerX, centerY );
		//mSuppMatrix.postScale( scale, scale);
		setImageMatrix( Command.Zoom, getImageViewMatrix() );
	}
	
	protected void postTranslate( float deltaX, float deltaY )
	{
		Log.d("deltaXY",deltaX+":"+deltaY);
		mSuppMatrix.postTranslate( deltaX, deltaY );
		setImageMatrix( Command.Move, getImageViewMatrix() );
	}
	
	protected void setImageMatrix( Command command, Matrix matrix )
	{
		setImageMatrix( matrix );
	}
	
	protected void zoomTo( float scale, final float centerX, final float centerY, final float durationMs )
	{
		 Log.d( "zoomTo", "zoomTo: " + scale + ", " + centerX + ": " + centerY );
		final long startTime = System.currentTimeMillis();
		final float incrementPerMs = ( scale - getScale() ) / durationMs;
		final float oldScale = getScale();
		mHandler.post( new Runnable() {
			
			public void run()
			{
				long now = System.currentTimeMillis();
				float currentMs = Math.min( durationMs, now - startTime );
				float target = oldScale + ( incrementPerMs * currentMs );
				zoomTo( target, centerX, centerY );
				if ( currentMs < durationMs ) {
					mHandler.post( this );
				} else {
					// if ( getScale() < 1f ) {}
				}
			}
		} );
	}
	
	protected float getValue( Matrix matrix, int whichValue )
	{
		matrix.getValues( mMatrixValues );
		return mMatrixValues[whichValue];
	}
	
	protected float getScale( Matrix matrix )
	{
		return getValue( matrix, Matrix.MSCALE_X );
	}
	
	public float getScale()
	{
		return getScale( mSuppMatrix );
	}
	
	
	public boolean onTouchEvent3(MotionEvent event) {
		return multiTouchController.onTouchEvent(event);
	}
	
	
	float mx, my;
	float curX, curY;
	float xPre,yPre;

	float xCur, yCur,      
     xSec, ySec,
     distDelta,
     distCur, distPre = -1;
	 
	 int mWidth, mHeight, mTouchSlop;
     long mLastGestureTime;
	 
	//@Override
	public boolean onTouchEvent2(MotionEvent event) {
		
		 int action = event.getAction() & MotionEvent.ACTION_MASK, 
         p_count = event.getPointerCount();
		
		 switch (action) {

         case MotionEvent.ACTION_DOWN:
             mx = event.getX();
             my = event.getY();
             touchState = DRAG;
             break;
         case MotionEvent.ACTION_MOVE:
             curX = event.getX();
             curY = event.getY();
             
             if (p_count > 1) {
                 // point 2 coords

             }
            
             break;
         case MotionEvent.ACTION_UP:
             int xDiff = (int) (mx -curX);
             int yDiff = (int) (my - curY);
         	
             Log.d("scroollTo",xDiff+","+ yDiff);
            
             scrollBy(xDiff, yDiff);
             
//             if(getScrollX() < 0 ) {
//             	xDiff = 0;
//             	scrollTo(0, getScrollY());
//             }
//             
//             if(getScrollY() < 0 ) {
//             	xDiff = 0;
//             	scrollTo(getScrollX(),0);
//             }
//             
//             if(getScrollX() > 200 ) {
//              	xDiff = 200;
//              	scrollTo(200, getScrollY());
//              }
             
             /*
             if(getScrollY() > 280 ) {
              	xDiff = 0;
              	scrollTo(getScrollX(),280);
              }
              */
             
                          
             Log.d("scroollXY",getScrollX()+","+ getScrollY());
             
             
             //Log.d("scroollXY",":"+switcherView.getDrawingCache().getWidth()+","+ switcherView.getDrawingCache().getHeight());
             
             //switcherView.getDrawingRect(outRect);
             
             //Log.d("scroollXY=>",Log.d("scroollXY",switcherView.getScrollX()+","+ switcherView.getScrollY()); );
             
             
//             if(switcherView.getScrollX() < 0) {
//             	xDiff = 0;
//             }
//             
//             if(switcherView.getScrollY() > 1028) {
//             	yDiff = 1028;                        	
//             }
             

             //switcherView.scrollTo(1024, 1024);
             
             
            

             mx = curX;
             my = curY;
             
//             Rect outRect = new Rect();
//            getDrawingRect(outRect); 
//             Log.d("rect","left=>"+outRect.left+","+outRect.top);
//             
//             
//             //BitmapDrawable bitmapDrawable = (BitmapDrawable)getResources().getDrawable(R.id.img1);
//             //Bitmap bitmap = bitmapDrawable.getBitmap();
//             Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.oldman);
//             if(bitmap !=null) {
//             	 Log.d("rect","bitmap=>"+bitmap.getWidth()+","+bitmap.getHeight());
//             }else {
//             	Log.d("rect","bitmap=> bitmap is null");
//             }
//             
//             int left = (outRect.left < 0)?0:outRect.left;
//             int top  = (outRect.right < 0)?0 : outRect.right;
//             
//             Bitmap createBitmap = Bitmap.createBitmap(bitmap, left, top, 300, 300);
//
//             setImageBitmap(createBitmap);
             
             
             break;
     }

     return true;
 
	}
	
	
	// calculate the distance
	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	// calculate the center point
	private void midPoint(PointF point, MotionEvent event) {
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		point.set(x / 2, y / 2);
	}

	

	
	
	
	

	@Override
	public Object getDraggableObjectAtPoint(PointInfo touchPoint) {
		// TODO Auto-generated method stub
		setTouchValueChanged(touchPoint);
		return this;
	}

	@Override
	public void getPositionAndScale(Object obj,
			PositionAndScale objPosAndScaleOut) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean setPositionAndScale(Object obj,
			PositionAndScale newObjPosAndScale, PointInfo touchPoint) {
		setTouchValueChanged(touchPoint);
		return true;
	}

	@Override
	public void selectObject(Object obj, PointInfo touchPoint) {
		// TODO Auto-generated method stub
		setTouchValueChanged(touchPoint);
	}
	
	private float beginPointX = -1f;
	private float beginPointY = -1f;
	private float movePointX = -1f;
	private float movePointY = -1f;

	
	public void setTouchValueChanged(PointInfo touchPoint) {		
		mCurrTouchPoint.set(touchPoint);
		
		
		
		if(!mCurrTouchPoint.isMultiTouch()) {
			
			if(mCurrTouchPoint.isDown()) {  //move
				beginPointX = touchPoint.getX();
				beginPointY = touchPoint.getY();
				
				scrollBy((int)beginPointX, (int)beginPointY);
				//Log.d("**2", beginPointX+","+beginPointY);
			}else {  //up
				
				
				//movePointX += touchPoint.getX() - beginPointX;
	    		//movePointY += touchPoint.getY() - beginPointY;
	    		
	    		//scrollBy((int)touchPoint.getX(), (int)touchPoint.getY());
				
				beginPointX = touchPoint.getX();
				beginPointY = touchPoint.getY();
				
	    		
	    		Log.d("**", touchPoint.getX()+","+touchPoint.getY());
	    		
			}
		}else {
			Log.d("##", movePointX+","+movePointY);
		}
		
		
		
	}
	
}
